#include "array.h"
#include <malloc.h>
#include <assert.h>
#include <string.h>

C_CODE_BEGIN
EXPORT_FUNC struct array* array_create( struct array* arr , size_t rsize , size_t objsize ) {
	arr->buffer= malloc( rsize * objsize );
	arr->objsize = objsize;
	arr->rsize = rsize;
	arr->usize = 0;
	return arr;
}

EXPORT_FUNC void array_destroy( struct array* arr ) {
	free(arr->buffer);
	arr->buffer= NULL;
	arr->rsize =arr->usize = 0;
	arr->objsize = 0;
}

static void _grow_and_cp( struct array* arr ,size_t rsize ) {
	char* buf;
	assert( rsize >= arr->usize );
	buf = malloc( arr->objsize*rsize );
	memcpy( (void*)buf , arr->buffer , arr->usize*arr->objsize );
	free(arr->buffer);
	arr->buffer = (void*)buf;
	arr->rsize = rsize;
}

EXPORT_FUNC void array_reserve( struct array* arr , size_t rsize ) {
	if ( rsize <= arr->rsize )
		return;
	_grow_and_cp(arr,rsize);
}

EXPORT_FUNC void array_push_back( struct array* arr , void* obj ) {
	assert( arr->rsize >= arr->usize );
	if ( arr->usize == arr->rsize ) {
		// grow
		array_reserve(arr,arr->rsize*2);
	}
	memcpy( ((char*)arr->buffer)+(arr->usize)*(arr->objsize), obj , arr->objsize );
	++(arr->usize);
}

EXPORT_FUNC void array_pop_back( struct array* arr ) {
	if( arr->usize != 0 ) {
		--(arr->usize);
	}
}

EXPORT_FUNC void array_expand( struct array* arr , size_t size ) {
	if (arr->rsize < size) {
		array_reserve( arr , size );
	}
	arr->rsize = size;
	arr->usize = size;
}

EXPORT_FUNC void* array_index_at( struct array* arr, int idx ) {
	assert( idx >=0 );
	return idx < (int)(arr->usize) ? ((char*)(arr->buffer)+ idx*(arr->objsize)) : NULL;
}

EXPORT_FUNC struct array* array_clone( struct array* src , struct array* des) {
	memcpy( des ,src, sizeof(struct array) );
	des->buffer = malloc( src->rsize*src->objsize );
	memcpy( des->buffer , src->buffer, (src->rsize)*(src->objsize) );
	return des;
}


EXPORT_FUNC void array_until( struct array* arr, bool (*func)(void* obj,int idx,void* others), void* others) {
	int idx = 0 ;
	while ( idx != arr->usize ) {
		if( func( (char*)(arr->buffer) + idx*(arr->objsize) , idx ,others) == false ) {
			return;
		}
		++idx;
	}
}

struct fewraper {
	void (*func)(void* node, int idx, void* others);
	void* par;

};

static INLINE bool _foreach_wrapper( void* obj,int idx,void* others ) {
	struct fewraper * fe = ( struct fewraper * )(others);
	fe->func( obj , idx , fe->par );
	return true;
}

EXPORT_FUNC void array_foreach ( struct array* arr , void (*func)(void*obj,int idx,void* others) , void* others) {
	struct fewraper fe;
	fe.func = func;
	fe.par =others;
	array_until(arr,_foreach_wrapper, &fe);
}

EXPORT_FUNC void array_trim( struct array* arr ) {
	_grow_and_cp(arr,arr->usize);
}

EXPORT_FUNC void array_clear( struct array* arr ) {
	arr->usize = 0;
}

C_CODE_END